
#include "camera_control.hpp"


bool g_bExit = false;

void InitSharedResources(SharedResourcesWrapper& sharedRes, int maxFrames) {
    if (!sharedRes.frameRes || !sharedRes.inferRes) {
        return;  // 检查指针是否为空
    }

    // 首先锁定第一个资源的互斥锁
    std::lock_guard<std::mutex> frameLock(sharedRes.frameRes->mutex);

    // 初始化原始图像帧共享资源
    sharedRes.frameRes->maxFrames = maxFrames;
    sharedRes.frameRes->newFrameAvailable = false;
    sharedRes.frameRes->frames.reserve(maxFrames);

    // 此时 frameLock 将超出作用域并自动释放 frameRes 互斥锁
    // 接着锁定第二个资源的互斥锁
    std::lock_guard<std::mutex> inferLock(sharedRes.inferRes->mutex);

    // 初始化推理结果共享资源
    sharedRes.inferRes->newInferenceResultAvailable = false;
    sharedRes.inferRes->infer_frame.reserve(maxFrames);
}

void DestroySharedResources(FrameSharedResources* frameRes, InferenceSharedResources* inferRes) {
    if (frameRes == nullptr || inferRes == nullptr) {
        return;  // 检查指针是否为空
    }

    // 锁定第一个资源的互斥锁
    std::lock_guard<std::mutex> frameLock(frameRes->mutex);
    {
        // 在 frameLock 的作用域内，安全地访问和修改原始图像帧共享资源
        frameRes->frames.clear();           // 清空图像帧向量
        frameRes->newFrameAvailable = false; // 重置新帧可用标志
    } // frameLock 作用域结束，自动解锁 frameRes->mutex

    // 此时 frameRes->mutex 已经解锁，接着锁定第二个资源的互斥锁
    std::lock_guard<std::mutex> inferLock(inferRes->mutex);
    {
        // 在 inferLock 的作用域内，安全地访问和修改推理结果共享资源
        inferRes->infer_frame.clear();               // 清空推理结果向量
        inferRes->newInferenceResultAvailable = false; // 重置新推理结果可用标志
    } // inferLock 作用域结束，自动解锁 inferRes->mutex
}

// 等待用户输入回车键以结束取流或结束程序
void PressEnterToExit(void)
{
    int c;
    while ( (c = getchar()) != '\n' && c != EOF );  // 清空输入缓冲区
    fprintf( stderr, "\nPress enter to exit.\n");   // 提示用户按回车退出
    while( getchar() != '\n');  // 等待用户按下回车键
    g_bExit = true;  // 设置全局退出标志为 true
    sleep(1);  // 稍作延迟，确保线程有时间检测到退出标志
}

// 打印设备信息
bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{
    if (NULL == pstMVDevInfo)
    {
        printf("The Pointer of pstMVDevInfo is NULL!\n");
        return false;
    }

    // 检查设备类型并打印相关信息
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    {
        // 提取和打印 GigE 相机的 IP 地址
        int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
        int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
        int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
        int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

        // 打印相机模型名称、IP地址和用户自定义名称
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);
        printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
        printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
    }
    else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    {
        // 打印 USB 相机的模型名称和用户自定义名称
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
        printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
    }
    else
    {
        printf("Not support.\n");
    }

    return true;
};

// 工作线程函数
void* WorkThread(void* args) {
    // 从 args 中提取 ThreadArgs 结构体
    ThreadArgs* threadArgs = static_cast<ThreadArgs*>(args);

    void* handle = threadArgs->handle;
    FrameSharedResources* pSharedResources = threadArgs->sharedResources;

    int nRet;
    MV_FRAME_OUT stImageInfo = {0};
    MV_DISPLAY_FRAME_INFO stDisplayInfo = {0};

    int frameCount = 0; // 图像的计数器
    while (!g_bExit) {
        nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 1000);
        if (nRet == MV_OK) {
            // std::cout << "Get One Frame: Width[" << stImageInfo.stFrameInfo.nWidth 
            //           << "], Height[" << stImageInfo.stFrameInfo.nHeight 
            //           << "], Frame Number[" << stImageInfo.stFrameInfo.nFrameNum << "]" << std::endl;

            cv::Mat matCorrected(stImageInfo.stFrameInfo.nHeight, 
                                 stImageInfo.stFrameInfo.nWidth, 
                                 CV_8UC1, 
                                 stImageInfo.pBufAddr);

            // 锁定互斥锁
            std::unique_lock<std::mutex> lock(pSharedResources->mutex);

            // 打印当前共享内存中帧的数量
            // std::cout << "Current frame buffer size: " << pSharedResources->frames.size() << std::endl;

            // 检查并剔除最老的数据，如果达到上限
            while (pSharedResources->frames.size() >= pSharedResources->maxFrames) {
                // std::cout << "Frame buffer is full. Removing the oldest frame." << std::endl;
                pSharedResources->frames.erase(pSharedResources->frames.begin());
            }

            // 创建新的图像帧共享指针
            auto matSharedPtr = std::make_shared<cv::Mat>(matCorrected.clone());

            // 添加新的图像帧到共享资源
            ImageFrame newFrame = {matSharedPtr, frameCount++, std::time(nullptr)};
            pSharedResources->frames.push_back(newFrame);
            pSharedResources->newFrameAvailable = true;

            // 打印添加新帧后共享内存中帧的数量
            // std::cout << "Added new frame. New frame buffer size: " << pSharedResources->frames.size() << std::endl;

            // 通知其他线程有新的帧数据可用
            pSharedResources->cond.notify_one();
        } else {
            // 打印获取图像失败的错误信息
            std::cerr << "Failed to get image frame. Error code: " << nRet << std::endl;
        }

        // 释放图像缓冲区
        nRet = MV_CC_FreeImageBuffer(handle, &stImageInfo);
        if (nRet != MV_OK) {
            std::cerr << "Failed to free image buffer. Error code: " << nRet << std::endl;
        }
    }

    std::cout << "Exiting WorkThread." << std::endl; // 线程结束时打印信息

    return nullptr; // 线程函数返回 nullptr
}

/**
 * 初始化系统函数
 * 
 * 该函数负责初始化SDK，枚举相机设备，选择并打开设备，以及配置相关参数。
 *
 * @param handle 指向相机句柄的指针，函数将创建并返回一个有效的句柄
 * @return 返回 MV_OK 表示成功，非零值表示失败
 */

int InitializeCamera(void** handle) {
    int nRet = MV_OK;
    MV_CC_DEVICE_INFO_LIST stDeviceList = {0};

    // 初始化 SDK
    nRet = MV_CC_Initialize();
    if (MV_OK != nRet) {
        printf("Initialize SDK fail! nRet [0x%x]\n", nRet);
        return nRet;
    }

    // 枚举设备
    nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
    if (MV_OK != nRet) {
        printf("MV_CC_EnumDevices fail! nRet [%x]\n", nRet);
        return nRet;
    }

    if (stDeviceList.nDeviceNum > 0) {
        printf("Please choose a camera index (0 - %u): ", stDeviceList.nDeviceNum - 1);
        unsigned int nIndex = 0;
        if (nIndex >= stDeviceList.nDeviceNum) {
            printf("Invalid camera index!\n");
            return -1;
        }

        // 选择设备并创建句柄
        nRet = MV_CC_CreateHandle(handle, stDeviceList.pDeviceInfo[nIndex]);
        if (MV_OK != nRet) {
            printf("MV_CC_CreateHandle fail! nRet [0x%x]\n", nRet);
            return nRet;
        }

        // 打开设备
        nRet = MV_CC_OpenDevice(*handle);
        if (MV_OK != nRet) {
            printf("MV_CC_OpenDevice fail! nRet [0x%x]\n", nRet);
            return nRet;
        }

        // 探测网络最佳包大小（只对GigE相机有效）
        if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE) {
            int nPacketSize = MV_CC_GetOptimalPacketSize(*handle);
            if (nPacketSize > 0) {
                nRet = MV_CC_SetIntValueEx(*handle, "GevSCPSPacketSize", nPacketSize);
                if (nRet != MV_OK) {
                    printf("Warning: Set Packet Size fail nRet [0x%x]!\n", nRet);
                }
            } else {
                printf("Warning: Get Packet Size fail nRet [0x%x]!\n", nPacketSize);
            }
        }

        // 设置触发模式为off
        nRet = MV_CC_SetEnumValue(*handle, "TriggerMode", 0);
        if (MV_OK != nRet) {
            printf("MV_CC_SetTriggerMode fail! nRet [0x%x]\n", nRet);
            return nRet;
        }

        // 开始取流
        nRet = MV_CC_StartGrabbing(*handle);
        if (MV_OK != nRet) {
            printf("MV_CC_StartGrabbing fail! nRet [0x%x]\n", nRet);
            return nRet;
        }
    } else {
        printf("No cameras found!\n");
        return -1;
    }

    return MV_OK;
}

/**
 * 等待用户输入函数
 * 
 * 该函数等待用户按下回车键，以终止程序。
 */
void WaitForExit() {
    PressEnterToExit();  // 等待用户按下回车键
}

/**
 * 资源清理函数
 * 
 * 该函数在程序结束时执行，用于停止图像抓取，关闭设备，销毁句柄，并反初始化SDK。
 *
 * @param handle 相机句柄，用于控制相机操作
 * @param nThreadID 工作线程ID，用于线程同步
 */
void Cleanup(void* handle, pthread_t nThreadID, SharedResourcesWrapper* sharedResWrapper) {
    
    int nRet = MV_OK;
    if (sharedResWrapper == nullptr) {
        return; // 检查指针是否为空
    }

    // 取消工作线程
    void* status = NULL;
    pthread_cancel(nThreadID);
    if (pthread_join(nThreadID, &status) != 0) {
        fprintf(stderr, "Failed to join work thread.\n");
    }

    // 停止取流
    nRet = MV_CC_StopGrabbing(handle);
    if (MV_OK != nRet) {
        fprintf(stderr, "MV_CC_StopGrabbing fail! nRet [0x%x]\n", nRet);
    }

    // 关闭设备
    nRet = MV_CC_CloseDevice(handle);
    if (MV_OK != nRet) {
        fprintf(stderr, "MV_CC_CloseDevice fail! nRet [0x%x]\n", nRet);
    }

    // 销毁句柄
    nRet = MV_CC_DestroyHandle(handle);
    if (MV_OK != nRet) {
        fprintf(stderr, "MV_CC_DestroyHandle fail! nRet [0x%x]\n", nRet);
    }

    printf("Program exited successfully.\n");
    // 销毁共享资源
    DestroySharedResources(sharedResWrapper->frameRes, sharedResWrapper->inferRes);

    // 反初始化SDK
    MV_CC_Finalize();

}

/**
 * 显示线程函数，用于在独立的线程中显示图像。
 * 
 * @param pUser 传递给线程的参数，这里是指向 FrameSharedResources 的指针。
 * @return 线程执行结束时返回 NULL。
 */
void* DisplayThread(void* pUser) {
    FrameSharedResources* frameRes = static_cast<FrameSharedResources*>(pUser);

    if (!frameRes) {
        return nullptr;
    }

    const char* windowName = "Live Video Feed";
    cv::namedWindow(windowName, cv::WINDOW_NORMAL);

    std::unique_lock<std::mutex> lock(frameRes->mutex, std::defer_lock); // 创建互斥锁，但不立即锁定

    while (!g_bExit) {
        frameRes->cond.wait(lock, [&frameRes]() {
            return frameRes->newFrameAvailable || g_bExit;
        });

        if (g_bExit) {
            break;
        }

        // 自动显示所有可用的新帧
        while (!frameRes->frames.empty()) {
            // 从vector中取出第一个图像帧
            ImageFrame& frame = frameRes->frames.front();
            cv::Mat frameToShow = *frame.mat; // 直接使用解引用操作符*获取cv::Mat实例

            // 显示图像帧
            cv::imshow(windowName, frameToShow);

            // 刷新显示窗口，并短暂等待（1ms），以便窗口可以响应键盘输入
            cv::waitKey(1);

            // 从vector中移除已显示的帧
            frameRes->frames.erase(frameRes->frames.begin());
            frameRes->newFrameAvailable = false; // 重置新帧可用标志
        }
    }

    cv::destroyWindow(windowName); // 销毁窗口
    return nullptr;
}

